package com.hmdp.service.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RedisConstants;
import com.hmdp.utils.RegexUtils;
import com.hmdp.utils.SystemConstants;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

	@Resource
	private StringRedisTemplate stringRedisTemplate;

	/**
	 * 实现发送验证码的方法
	 */
	@Override
	public Result sendCode(String phone, HttpSession session) {
		// 1.校验手机号格式
		if (RegexUtils.isPhoneInvalid(phone)) {
			// 2.不符合，返回错误信息
			return Result.fail("手机号格式错误！");
		}

		// 3.符合，使用hutool的工具类生成验证码
		String code = RandomUtil.randomNumbers(6);

		// 4.保存验证码到redis
		stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code,
				RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);

		// 5.模拟发送验证码
		log.debug("模拟发送验证码:{}", code);

		// 返回OK
		return Result.ok();
	}

	/**
	 * 实现登录的方法
	 */
	@Override
	public Result login(LoginFormDTO loginForm, HttpSession session) {
		// 1.校验登录手机号格式
		String phone = loginForm.getPhone();
		if (RegexUtils.isPhoneInvalid(phone)) {
			// 1-2.不符合，返回错误信息
			return Result.fail("手机号格式错误！");
		}

		// 2.从redis获取刚才给用户发送的验证码与用户填写返回来的验证码进行对比校验
		String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);
		String code = loginForm.getCode();
		if (cacheCode == null || !cacheCode.equals(code)) {
			// 3.不一致，返回异常
			return Result.fail("验证码错误！");
		}

		// 4.一致，根据手机号查询用户 select * from tb_user where phone = ?
		User user = query().eq("phone", phone).one();
		log.debug("根据登录手机号查询用户:{}", user);

		// 5.判断用户是否存在
		if (user == null) {
			log.debug("手机号:{}尚未注册，开始进行注册", phone);
			// 6.创建用户并保存
			user = createUserWithPhone(phone);
			log.debug("注册完成");
		}

		// 7.保存用户信息到Redis
		// 7.1随机生成token，作为登录令牌
		String token = UUID.randomUUID().toString(true);
		log.debug("生成随机token:{}", token);

		// 7.2将User对象转换成HashMap储存
		UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
		Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(), CopyOptions.create()
				.setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));

		// 7.3保存数据到redis中,putAll使用Map一次性存多个字段
		stringRedisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY + token, userMap);
		log.debug("已将用户(UserDTO)信息:{}保存到redis", userMap);

		// 7.4设置token有效期
		stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL,
				TimeUnit.MINUTES);
		log.debug("设置token有效期为:{}分钟", RedisConstants.LOGIN_USER_TTL);

		// 8.返回token
		return Result.ok(token);
	}

	/**
	 * 根据手机号注册用户
	 * 
	 * @param phone
	 * @return
	 */
	private User createUserWithPhone(String phone) {
		// 创建用户
		User user = new User();
		user.setPhone(phone);
		user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));

		// 保存用戶
		save(user);
		return user;
	}

}
